package com.shiqi.order.controller;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;

/**
 *
 */
@RestController
@RequestMapping("/order")
public class OrderController {

    private static final String RESOURCE_NAME = "order-sentinel";
    private static final String ANNOTATION_RESOURCE_NAME = "order-sentinel-annotation";
    private static final String DEGRADE_RESOURCE = "degrade";

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/add")
    public String add() {
        System.out.println("下单成功");
        String result = restTemplate.getForObject("http://localhost:8081/stock/reduce", String.class);
        System.out.println(result);
        return "success";
    }


    @GetMapping("/testSentinelCore")
    public String testSentinelCore() {
        Entry entry = null;
        try {
            entry = SphU.entry(RESOURCE_NAME);
            System.out.println("hello sentinel");
        } catch (BlockException e) {
            e.printStackTrace();
            return "被限流了";
        } catch (Exception e) {
            // 若需要配置降级规则，需要通过这种方式记录业务异常
            Tracer.traceEntry(e, entry);
        } finally {
            if (entry != null) {
                entry.exit();
            }
        }
        return "success";
    }

    @GetMapping("/testSentinelCoreByAnnotation")
    @SentinelResource(value = ANNOTATION_RESOURCE_NAME, blockHandler = "blockHandlerFromId", fallback = "fallbackHandlerFromId")
    public String testSentinelCoreByAnnotation(String id) {
        System.out.println(id);
        int i = 1/0;
        return "success";
    }

    @GetMapping("/testSentinelCoreDegrade")
    @SentinelResource(value = DEGRADE_RESOURCE, entryType = EntryType.IN, blockHandler = "blockHandlerDegrade")
    public String testSentinelCoreDegrade(String id) {
        System.out.println("测试降级");
        int i = 1/0;
        return "success";
    }

    public String fallbackHandlerFromId(String id, Throwable e) {
        System.out.println("接口异常");
        e.printStackTrace();
        return id + "接口异常";
    }

    public String blockHandlerDegrade(String id, BlockException e) {
        System.out.println("降级");
        e.printStackTrace();
        return id + "被降级";
    }

    public String blockHandlerFromId(String id, BlockException e) {
        System.out.println("限流");
        e.printStackTrace();
        return id + "被限流";
    }

    @PostConstruct
    private static void initFlowRules() {
        List<FlowRule> rules = new ArrayList<>();
//        // 流控
//        FlowRule rule = new FlowRule();
//        // 设置受保护的资源（为哪个资源进行流控）
//        rule.setResource(RESOURCE_NAME);
//        // 设置流控规则qps每秒访问量
//        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
//        // 设置受保护的资源阈值，即每秒的访问数上线
//        rule.setCount(1);
        rules.add(getFlowRule(RESOURCE_NAME, RuleConstant.FLOW_GRADE_QPS, 1));
        rules.add(getFlowRule(ANNOTATION_RESOURCE_NAME, RuleConstant.FLOW_GRADE_QPS, 1));
        // 加载配置好的流控规则
        FlowRuleManager.loadRules(rules);
    }

    @PostConstruct
    private static void initDegradeRules() {
        List<DegradeRule> degradeRules = new ArrayList<>();
        degradeRules.add(getDegradeRule(DEGRADE_RESOURCE, RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT , 2));
        DegradeRuleManager.loadRules(degradeRules);
    }

    /**
     *
     * @param resourceName 资源名
     * @param grade 规则策略
     * @param count 规则侧率
     * @return
     */
    private static DegradeRule getDegradeRule(String resourceName, int grade, int count) {
        DegradeRule degradeRule = new DegradeRule();
        switch (grade) {
            case RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT : // 异常比例
                degradeRule.setResource(resourceName);
                degradeRule.setCount(count); // 异常数
                degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
                degradeRule.setTimeWindow(10); // 熔断时长，触发熔断后保持时间，时间过后半开状态，第一次请求还异常，继续熔断
                degradeRule.setMinRequestAmount(2); // 最小请求数 2次请求发生3次异常进行熔断
                // degradeRule.setStatIntervalMs(10 * 6000); // 统计时长 默认1秒
                break;
            default:
                break;
        }
        return degradeRule;
    }

    private static FlowRule getFlowRule(String resourceName, int grade, int count) {
        FlowRule rule = new FlowRule();
        switch (grade) {
            case RuleConstant.FLOW_GRADE_QPS :
                rule.setResource(resourceName);
                rule.setGrade(grade);
                rule.setCount(count);
                break;
            default:

        }
        return rule;
    }
}
